<?php

namespace common\models;

use common\models\CenterInProject;
use common\models\Project;
use common\models\ProjectAuthorization;
use common\models\Tab;
use common\models\TabCheck;
use common\models\RepeatSubject;
use common\models\User;
use common\models\SubjectActionLog;
use common\models\visit\VisitConfime;
use common\models\visit\VisitDefineConfig;
use Yii;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;

/**
 * This is the model class for table "subject".
 * Subject，受试者实体类
 *
 * @author        Bran <lin.yang@ht-med.com>
 * @copyright (c) 2015, HT-MED
 * @package       common.models
 * @since         version 1.0
 *
 * @property integer $seq
 * @property integer $project_id
 * @property integer $cip_id
 * @property string $subjid
 * @property string $no
 * @property string $screenid
 * @property string $name
 * @property string $user_id
 * @property integer $inver
 * @property string $registration_date
 * @property integer $visit_id
 * @property integer $crc
 * @property integer $treatment_id
 * @property integer $status
 * @property integer $ae
 * @property integer $sae
 * @property integer $created_at
 * @property integer $signed_at
 * @property integer $locked_at
 * @property integer $updated_at
 * @property integer $enter_at
 */
class Subject extends ActiveRecord
{

    /**
     * 受试者的各种状态
     */
    const STATUS_STARTED = 1;//进行中
    const STATUS_STOPED = 2;//已中止
    const STATUS_COMPLETED = 3;//已完成
    const STATUS_SIGNED = 4;//已签认
    const STATUS_LOCKED = 5;//已锁定
    const STATUS_INVALID = 6;//无效

    /**
     * 受试者各种状态数组的getter方法
     *
     * @return array 受试者各种状态数组
     */
    public function getStatuses()
    {
        return [
            self::STATUS_STARTED => Yii::t('subject', 'Pending'),
            self::STATUS_STOPED => Yii::t('subject', 'Stoped'),
            self::STATUS_COMPLETED => Yii::t('subject', 'Completed'),
            self::STATUS_SIGNED => Yii::t('subject', 'Signed'),
            self::STATUS_LOCKED => Yii::t('subject', 'Locked'),
            self::STATUS_INVALID => Yii::t('subject', 'Invalid'),
        ];
    }


    /**
     * 患者关联状态
     */
    const LAB_COR_OK = 1;
    const LAB_COR_NO = 2;

    public static function getLabCor()
    {
        return [
            self::LAB_COR_OK => '√',
            self::LAB_COR_NO => '×',
        ];
    }

    public function getCrcs()
    {
        $centers = $this->getCenters();
        $in_array = array_keys($centers);
        $items = ProjectAuthorization::find()->where(['and',
            ['in', 'cip_id', $in_array],
            ['project_authorization.role' => 'CRC'],
            ['user.status' => User::STATUS_ACTIVE],
            ['project_authorization.is_auth' => 1]
        ])
            ->joinWith('user')
            ->all();
        $crcs = [];
        foreach ($items as $model) {
            $crcs[$model->user_id] = $model->user->name;
        }
        return $crcs;
    }

    /**
     * 可手动修改的受试者各种状态数组的getter方法
     *
     * @return array 受试者各种状态数组
     */
    public function getEditableStatuses()
    {
        return [
            self::STATUS_STARTED => Yii::t('subject', 'Pending'),
            self::STATUS_STOPED => Yii::t('subject', 'Stoped'),
            self::STATUS_COMPLETED => Yii::t('subject', 'Completed'),
            self::STATUS_INVALID => Yii::t('subject', 'Invalid'),
        ];
    }

    public function getCenters()
    {
        $items = CenterInProject::find()->where(['project_id' => Yii::$app->session->get('defaultPid'), 'status' => CenterInProject::STATUS_ACTIVE])->all();
        $centers = [];
        foreach ($items as $model) {
            $centers[$model->id] = $model->center->center;
        }
        return $centers;
    }

    public function getInstitutes()
    {
        $items = CenterInProject::find()->where([
            'project_id' => Yii::$app->session->get('defaultPid'),
            'type' => CenterInProject::TYPE_INV,
            'status' => CenterInProject::STATUS_ACTIVE
        ])->all();
        $centers = [];
        foreach ($items as $model) {
            $centers[$model->id] = $model->center->center;
        }
        return $centers;
    }

    /**
     * AR关联表，CenterInProject对象的getter方法
     *
     * @return CenterInProject $cip
     */
    public function getCip()
    {
        return $this->hasOne(CenterInProject::className(), ['id' => 'cip_id']);
    }

    /**
     * AR关联表，Project对象的getter方法
     *
     * @return Project $project
     */
    public function getProject()
    {
        return $this->hasOne(Project::className(), ['id' => 'project_id']);
    }

    /**
     * AR关联表，User对象的getter方法
     *
     * @return User $inv
     */
    public function getInv()
    {
        return $this->hasOne(User::className(), ['id' => 'inver']);
    }

    /**
     * AR关联表，User对象的getter方法
     *
     * @return User $crc
     */
    public function getCrcName()
    {
        return $this->hasOne(User::className(), ['id' => 'crc']);
    }

    /**
     * AR关联表，RepeatSubject对象的getter方法
     *
     * @return RepeatSubject RepeatSubject对象
     */
    public function getRepeat()
    {
        return $this->hasOne(RepeatSubject::className(), ['seq' => 'seq']);
    }

    public function getVersion()
    {
        return $this->hasOne(DbVersion::className(), ['id' => 'version_id']);
    }

    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return 'subject';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['cip_id', 'user_id', 'project_id', 'inver', 'visit_id', 'treatment_id', 'status', 'ae', 'sae', 'created_at', 'signed_at', 'locked_at', 'updated_at', 'crc', 'informed_id'], 'integer'],
            [['registration_date', 'consent_at'], 'safe'],
            [['subjid', 'screenid'], 'string', 'max' => 30],
            [['name'], 'string', 'max' => 4],
            [['no'], 'string', 'max' => 20],
            [['no'], 'checkNo'],
            ['screenid', 'checkScreenid'],
            ['screenid', 'match', 'pattern' => '/^[A-Za-z0-9-_]+$/', 'message' => '筛选号只能包含字母、数字、_ 、- 任意组合'],
            [['status'], 'default', 'value' => self::STATUS_STARTED],
            [['user_id'], 'default', 'value' => Yii::$app->user->identity->id],
            [['version_id'], 'default', 'value' => 0],
            [['project_id'], 'default', 'value' => Yii::$app->session->get('defaultPid')],
            [['cip_id', 'screenid', 'user_id', 'inver', 'registration_date', 'status', 'name', 'version_id'], 'required'],
        ];
    }

    //验证受试者病历号
    public function checkNo($attribute, $params)
    {
        if ($this->no) {
            $subject = self::findOne(['no' => $this->no, 'cip_id' => $this->cip_id]);
            if ($subject && $subject->seq != $this->seq) {
                $this->addError($attribute, "受试者的病例号不能重复");
                return false;
            }
        }
        return true;
    }

    //验证随机号
    public function checkScreenid($attribute, $params)
    {
        if (!$this->seq) {
            $project = Subject::findOne([
                'project_id' => Yii::$app->session->get('defaultPid'),
                'screenid' => $this->screenid,
            ]);
            if ($project) {
                $this->addError($attribute, "受试者的筛选号不能重复");
                return false;
            }
        } else {
            $project = Subject::findOne([
                'seq' => $this->seq,
            ]);
            if ($project['screenid'] != $this->screenid) {
                $project = Subject::findOne([
                    'project_id' => Yii::$app->session->get('defaultPid'),
                    'screenid' => $this->screenid,
                ]);
                if ($project) {
                    $this->addError($attribute, "受试者的筛选号不能重复");
                    return false;
                }
            }

        }
        return true;
    }

    /**
     * @inheritdoc
     */
    public function behaviors()
    {
        return [
            TimestampBehavior::className(),
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'seq' => Yii::t('subject', 'Seq'),
            'cip_id' => Yii::t('subject', 'cip_id'),
            'subjid' => Yii::t('subject', 'subjid'),
            'screenid' => Yii::t('subject', 'screenid'),
            'name' => Yii::t('subject', 'name'),
            'user_id' => Yii::t('subject', 'user_id'),
            'inver' => Yii::t('subject', 'inver'),
            'registration_date' => Yii::t('subject', 'registration_date'),
            'visit_id' => Yii::t('subject', 'visit_id'),
            'treatment_id' => Yii::t('subject', 'treatment_id'),
            'status' => Yii::t('subject', 'status'),
            'ae' => Yii::t('subject', 'ae'),
            'sae' => Yii::t('subject', 'sae'),
            'created_at' => Yii::t('subject', 'created_at'),
            'signed_at' => Yii::t('subject', 'signed_at'),
            'locked_at' => Yii::t('subject', 'locked_at'),
            'crc' => Yii::t('subject', 'crc'),
            'no' => '病历号',
            'consent_at' => Yii::t('subject', 'consent_at'),
            'informed_id' => Yii::t('subject', 'informed_id'),
            'version_id' => Yii::t('subject', 'version_id'),
        ];
    }

    public function fields()
    {
        return [
            'seq',
            'cid' => function ($data) {
                return $data->cip_id;
            },
            'invid' => function ($data) {
                return $data->cip->invid;
            },
            'site' => function ($data) {
                return $data->cip->center->center;
            },
            'screenid',
            'subjid',
            'name',
            'registration_date',
            'status',
            'crc',
            'updated_at',
        ];
    }

    /**
     * 获取该项目本中心研究者列表
     *
     * @return array 研究者列表数组
     */
    public function getInvers()
    {
        $auth = ProjectAuthorization::find()
            ->with('user')
            ->where([
                'cip_id' => getSession('defaultCipid'),
                'role' => ['PI', 'SubI'],
                'is_auth' => 1
            ])->all();
        return \yii\helpers\ArrayHelper::map($auth, 'user.id', 'user.name');
    }

    /**
     * 获取本项目的数据库版本
     */
    public function getDbVersion()
    {
        $project_id = getSession('defaultPid');
        $isDbVersion = DbVersion::getProjectVersionAll();
        if( $isDbVersion === true ){
            $list = DbVersion::find()->where(['project_id' => $project_id, 'status' => DbVersion::STATUS_ACTIVE])->select('id,version,name')->all();
            $arr = [];
            if (!empty($list)) {
                foreach ($list as $k => $v) {
                    $name = empty($v->name) ? '版本' : $v->name;
                    $arr[$v->id] = $v->version;
                }

            }
            return $arr;
        }

        if (getProjectIsOld() == 1) {
            return [0 => Yii::t('database', 'Default Version')];
        } else {
            return [];
        }


    }

    /**
     * 获取该项目本中心研究者列表
     *
     * @return array 研究者列表数组
     */
    public function isLabCor($id)
    {
        $auth = DmLabCor::findOne(['seq' => $id]);
        if ($auth) {
            return 1;
        } else {
            return 2;
        }
    }

    /**
     * 生成受试者的tab_check表数据
     *
     * @return [type] [description]
     */
    public function generateTabCheck()
    {
        $tabs = Tab::find()->where(['project_id' => Yii::$app->session->get('defaultPid')])->all();
        $sql = 'INSERT INTO tab_check(`seq`,`tab_id`,`visit_id`,`status`) VALUES';
        foreach ($tabs as $tab) {
            $visits = explode(',', $tab->visits);
            foreach ($visits as $visit) {
                $sql .= '(' . $this->seq . ',' . $tab->id . ',' . $visit . ',0),';
            }
        }
        $sql = rtrim($sql, ',');
        $command = Yii::$app->db->createCommand($sql);
        return $command->execute();
    }

    /**
     * 研究进展情况汇总统计
     *
     * @return array 各种统计数字数组
     */
    public function sum($project_id = null)
    {
        if (!$project_id) {
            $pid = Yii::$app->session->get('defaultPid') ? Yii::$app->session->get('defaultPid') : 0;
        } else {
            $pid = $project_id;
        }
        $sqlOfUnrandomProject = "SELECT invid,center,`number`,COUNT(*) AS screened,COUNT(*) AS enrolled,
		ROUND(100*COUNT(*)/`number`,2) AS enRate,SUM(sae) AS saes,SUM(ae) AS aes,
	SUM(CASE WHEN t.status = 1 THEN 1 ELSE 0 END) AS started,SUM(CASE WHEN t.status = 2 THEN 1 ELSE 0 END) stoped,
  SUM(CASE WHEN t.status = 3 THEN 1 ELSE 0 END) AS completed,SUM(CASE WHEN t.status = 4 THEN 1 ELSE 0 END) singned,
  SUM(CASE WHEN t.status = 5 THEN 1 ELSE 0 END) AS locked, ROUND(100*((COUNT(*) - SUM(CASE WHEN t.status = 1 THEN 1 ELSE 0 END))/COUNT(*)),2) AS fnRate
FROM center_in_project i
LEFT JOIN subject t
ON i.id = cip_id
LEFT JOIN center c
ON c.id = i.center_id
WHERE t.project_id = " . $pid . " and i.type = " . CenterInProject::TYPE_INV . " AND t.status < " . self::STATUS_INVALID . "
GROUP BY i.id
ORDER BY invid";

        $sqlOfRandomProject = "SELECT invid,center,`number`,COUNT(*) AS screened,COUNT(*) - SUM(ISNULL(subjid)) AS enrolled,
    ROUND(100*((COUNT(*) - SUM(ISNULL(subjid)))/`number`),2) AS enRate,SUM(sae) AS saes,SUM(ae) AS aes,
    SUM(CASE WHEN t.status = 1 THEN 1 ELSE 0 END) AS started,SUM(CASE WHEN t.status = 2 THEN 1 ELSE 0 END) stoped,
    SUM(CASE WHEN t.status = 3 THEN 1 ELSE 0 END) AS completed,SUM(CASE WHEN t.status = 4 THEN 1 ELSE 0 END) singned,
    SUM(CASE WHEN t.status = 5 THEN 1 ELSE 0 END) AS locked, ROUND(100*((COUNT(*) - SUM(CASE WHEN t.status = 1 THEN 1 ELSE 0 END))/COUNT(*)),2) AS fnRate
    FROM center_in_project i
    LEFT JOIN subject t
    ON i.id = cip_id
    LEFT JOIN center c
    ON c.id = i.center_id
    WHERE t.project_id = " . $pid . " and i.type = " . CenterInProject::TYPE_INV . " AND t.status < " . self::STATUS_INVALID . "
    GROUP BY i.id
    ORDER BY invid";

        $project = Project::findOne($pid);

        $sql = isset($project) && $project->random == Project::PROJECT_RANDOM_ONE ? $sqlOfUnrandomProject : $sqlOfRandomProject;
        $command = Yii::$app->db->createCommand($sql);
        return $command->queryAll();
    }

    /**
     * 签名受试者
     *
     * @param SubjectActionLog $log SubjectActionLog对象
     * @return [type]       [description]
     */
    public function signture($log)
    {
        if (in_array($this->status, [self::STATUS_SIGNED, self::STATUS_LOCKED])) {
            return true;
        }
        $this->status = Subject::STATUS_SIGNED;
        $this->signed_at = time();
        if ($this->save()) {
            SubjectActionLog::insertLog($this->seq, SubjectActionLog::OBJECT_SUBJECT, SubjectActionLog::ACTION_SIGN, $log);
            return $this->signtureTabChecks();
        } else {
            return false;
        }
    }

    /**
     * 签名本受试者的全部数据域
     *
     * @return boolean [description]
     */
    public function signtureTabChecks()
    {
        $checks = TabCheck::find()->where(['seq' => $this->seq])->all();
        $result = true;
        foreach ($checks as $model) {
            $model->status = TabCheck::STATUS_SIGNED;
            $result = $result && $model->save();
        }
        return $result;
    }

    /**
     * 锁定受试者
     *
     * @param SubjectActionLog $log SubjectActionLog对象
     * @return boolean
     */
    public function lock($log)
    {
        if ($this->status != self::STATUS_SIGNED) {
            return true;
        }
        $this->status = Subject::STATUS_LOCKED;
        $this->locked_at = time();
        if ($this->save()) {
            SubjectActionLog::insertLog($this->seq, SubjectActionLog::OBJECT_SUBJECT, SubjectActionLog::ACTION_LOCK, $log);
            return $this->lockTabChecks();
        } else {
            return false;
        }
    }

    /**
     * 锁定本受试者的全部数据域
     *
     * @return boolean
     */
    public function lockTabChecks()
    {
        $checks = TabCheck::find()->where(['seq' => $this->seq])->all();
        $result = true;
        foreach ($checks as $model) {
            $model->status = TabCheck::STATUS_LOCKED;
            $result = $result && $model->save();
        }
        return $result;
    }

    /**
     * 解锁受试者
     *
     * @param SubjectActionLog $log SubjectActionLog对象
     * @return boolean
     */
    public function deblock($log)
    {
        if ($this->status != self::STATUS_LOCKED) {
            return true;
        }
        $this->status = Subject::STATUS_SIGNED;
        $this->locked_at = time();
        if ($this->save()) {
            SubjectActionLog::insertLog($this->seq, SubjectActionLog::OBJECT_SUBJECT, SubjectActionLog::ACTION_DEBLOCK, $log);
            return $this->deblockTabChecks();
        } else {
            return false;
        }
    }

    /**
     * 解锁本受试者的全部数据域
     *
     * @return boolean
     */
    public function deblockTabChecks()
    {
        $checks = TabCheck::find()->where(['seq' => $this->seq])->all();
        $result = true;
        foreach ($checks as $model) {
            $model->status = TabCheck::STATUS_SIGNED;
            $result = $result && $model->save();
        }
        return $result;
    }

    /**
     * 对CRF的批量操作
     *
     * @param string $keys 受试者seq的组合字符串
     * @param string $action 操作
     * @param SubjectActionLog $log SubjectActionLog对象
     * @return boolean
     */
    public function batchAction($keys, $action, $log)
    {
        $seqs = explode(',', $keys);
        $result = true;
        foreach ($seqs as $seq) {
            $subject = self::find()->where(['seq' => intval($seq)])->andWhere(['<', 'status', self::STATUS_INVALID])->one();
            if ($subject) {
                switch ($action) {
                    case 'signture':
                        $result = $result && $subject->signture($log);
                        break;
                    case 'lock':
                        $result = $result && $subject->lock($log);
                        break;
                    case 'deblock':
                        $result = $result && $subject->deblock($log);
                        break;
                    default:
                        break;
                }
            }
        }
        return $result;
    }

    public function getDateRange()
    {
        $start = self::find()->joinWith(['cip'])->where([
            't.project_id' => Yii::$app->session->get('defaultPid')
        ])->orderBy('registration_date')->one();
        $end = self::find()->joinWith(['cip'])->where([
            't.project_id' => Yii::$app->session->get('defaultPid')
        ])->orderBy('registration_date DESC')->one();
        return [isset($start) ? $start->registration_date : date('Y-m-d'), isset($end) ? $end->registration_date : date('Y-m-d')];
    }

    public function getData()
    {
        $sources = $this->sources;
        $numbers = $sources['numbers'];
        $nums = [];
        $last = array_fill_keys($this->institutes, 0);

        foreach ($this->institutes as $center) {
            foreach ($sources['dates'] as $date) {
                $delta = isset($numbers[$center . $date]) ? $numbers[$center . $date] : 0;
                $nums[$center][] = $delta + $last[$center];
                $last[$center] = $delta + $last[$center];
            }
        }

        return [
            'nums' => $nums,
            'dates' => $sources['dates'],
            'numbers' => $numbers,
        ];
    }

    public function getSources()
    {
        $sql = "SELECT center,registration_date,count(*) as num
            FROM `subject` t
            LEFT JOIN center_in_project i
            ON i.id = t.cip_id
            LEFT JOIN center c
            ON c.id = i.center_id
            WHERE t.project_id = " . Yii::$app->session->get('defaultPid') . " AND t.status < " . self::STATUS_INVALID . "
            GROUP BY `registration_date`,center";
        $command = Yii::$app->db->createCommand($sql);
        $numbers = [];
        $dates = [];
        foreach ($command->queryAll() as $key => $value) {
            $dates[] = $value['registration_date'];
            $numbers[$value['center'] . $value['registration_date']] = $value['num'];
        };
        return [
            'dates' => array_unique($dates),
            'numbers' => $numbers,
        ];
    }

    /*
     * 创建或修改受试者的时候创建访视信息
     *
     * return boolean
    */
    public function afterSave($insert, $changedAttributes)
    {
        parent::afterSave($insert, $changedAttributes);
        if ($insert) {
            $this->saveConfime(null, $this->seq, $this->registration_date);
        }
        if ($this->crc > 0) {
            $confime = new VisitConfime();
            $confime = $confime::findAll(['seq' => $this->seq]);
            if (empty($confime)) {
                $this->saveConfime(null, $this->seq, $this->registration_date);
            }
        }
        if (isset($changedAttributes['status'])) {
            $confime = new VisitConfime();
            $confime = $confime::findAll(['seq' => $this->seq]);
            $status = $this->status;
            if (!empty($confime)) {
                foreach ($confime as $value) {
                    $value->status = $status == 6 ? 3 : ($value->status == 3 ? ($value->note ? 2 : 1) : $value->status);
                    $value->save();
                }
            }
        }
    }

    /**
     * 批量保存每个受试者的提醒信息
     *
     * $project
     * $seq
     *
     * @return boolean
     */
    public function saveConfime($project = null, $seq, $registration_date)
    {
        if (!$project) {
            $project = Yii::$app->session->get('defaultPid');
        }
        $visitDefineConfig = new VisitDefineConfig();
        $visitDefineConfig = $visitDefineConfig::findAll(['project_id' => $project]);
        $error = [];
        foreach ($visitDefineConfig as $value) {
            $error[] = $this->saveConfimeValue($value, $seq, $registration_date);
        }
        return true;
    }

    /**
     * 保存每个受试者的提醒信息
     *
     * @return boolean
     */
    private function saveConfimeValue($value, $seq, $registration_date)
    {
        if ($value->base == null || $value->float == null) {
            return true;
        }
        $confime = new VisitConfime();
        $confime->seq = $seq;
        $confime->visit_id = $value->visit_id;
        $confime->plan_time = strtotime($registration_date);
        $confime->save();
        return true;
    }

    /**
     * 受试者是否存在query
     *
     * @author syy
     * @date 2020.8.5
     */
    public static function hasNewQuery($seq)
    {
        $where['project_id'] = Yii::$app->session->get('defaultPid');
        $where['seq'] = $seq;
        return Helper::hasNewQuery($where);
    }


    /**
     * 受试者下数据域状态展示
     *
     * @author syy
     * @date 2020.8.7
     */
    public static function varbAllStatus($seq)
    {
        $allStatus = TabCheck::getTabAllStatus($seq);
        /*是否有质疑*/
        $query = ' <span title="有质疑" class="glyphicon glyphicon-exclamation-sign status' . self::hasNewQuery($seq) . '" aria-hidden="true"></span>';

        /*是否sdv*/
        $sdv = $allStatus['sdv'] ?
            ' <span title="待SDV" class="glyphicon glyphicon-check" aria-hidden="true" style="color:gray"></span>'
            :
            ' <span title="已SDV" class="glyphicon glyphicon-check" aria-hidden="true" style="color:green"></span>';
        /*是否核查*/
        $checked = $allStatus['checked']
            ?
            ' <span title="待核查" class="glyphicon glyphicon-ok-sign" style="color:gray"></span>'
            :
            ' <span title="已核查" class="glyphicon glyphicon-ok-sign" style="color:green" ></span>';

        $tabCheck = new TabCheck;
        $status = '<span class="glyphicon glyphicon-' . $tabCheck->icons[$allStatus['statusInt']] .
            '" aria-hidden="true" title="' . $tabCheck->titles[$allStatus['statusInt']] . '" style="color:' . $tabCheck->colors[$allStatus['statusInt']] . ';"></span> ';

        return $status . $query . $sdv . $checked;
    }


}
